package com.hzb.erp.api.pc.lesson.service.impl;

import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzb.erp.api.pc.clazz.entity.ClassStudent;
import com.hzb.erp.api.pc.clazz.entity.Clazz;
import com.hzb.erp.api.pc.clazz.mapper.ClassStudentMapper;
import com.hzb.erp.api.pc.course.entity.Course;
import com.hzb.erp.api.pc.course.mapper.CourseMapper;
import com.hzb.erp.api.pc.lesson.entity.Lesson;
import com.hzb.erp.api.pc.lesson.entity.LessonStudent;
import com.hzb.erp.api.pc.lesson.mapper.LessonMapper;
import com.hzb.erp.api.pc.lesson.mapper.LessonStudentMapper;
import com.hzb.erp.api.pc.lesson.pojo.LessonEvaluateSaveDTO;
import com.hzb.erp.api.pc.lesson.pojo.LessonStudentCountsVO;
import com.hzb.erp.api.pc.lesson.pojo.LessonStudentParamDTO;
import com.hzb.erp.api.pc.lesson.pojo.LessonStudentVO;
import com.hzb.erp.api.pc.lesson.service.LessonStudentService;
import com.hzb.erp.api.pc.student.entity.Student;
import com.hzb.erp.api.pc.student.entity.StudentCourse;
import com.hzb.erp.api.pc.student.entity.StudentCreditLog;
import com.hzb.erp.api.pc.student.mapper.StudentMapper;
import com.hzb.erp.api.pc.student.pojo.StudentCourseVO;
import com.hzb.erp.api.pc.student.service.StudentCourseService;
import com.hzb.erp.api.pc.student.service.StudentLessonCountLogService;
import com.hzb.erp.api.pc.student.service.StudentService;
import com.hzb.erp.api.pc.sys.service.NotificationService;
import com.hzb.erp.common.entity.Staff;
import com.hzb.erp.common.enums.*;
import com.hzb.erp.common.mapper.StaffMapper;
import com.hzb.erp.common.service.SettingService;
import com.hzb.erp.exception.BizException;
import com.hzb.erp.sysservice.enums.SettingNameEnum;
import com.hzb.erp.sysservice.notification.NoticeCodeEnum;
import com.hzb.erp.sysservice.notification.bo.LessonEvaluateBO;
import com.hzb.erp.utils.CommonUtil;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 课时学员关联表 服务实现类
 * </p>
 *
 * @author Ryan
 */
@Service
public class LessonStudentServiceImpl extends ServiceImpl<LessonStudentMapper, LessonStudent> implements LessonStudentService {

    @Autowired
    private StudentCourseService studentCourseService;
    @Autowired
    private LessonStudentMapper lessonStudentMapper;
    @Autowired
    private StudentLessonCountLogService studentLessonCountLogService;
    @Autowired
    private StudentService studentService;
    @Resource
    private LessonMapper lessonMapper;
    @Resource
    private StudentMapper studentMapper;
    @Resource
    private StaffMapper staffMapper;
    @Resource
    private SettingService settingService;
    @Resource
    private ClassStudentMapper classStudentMapper;
    @Resource
    private CourseMapper courseMapper;
    @Resource
    private NotificationService notificationService;

    @Override
    public IPage<LessonStudentVO> getList(LessonStudentParamDTO param) {
        IPage<LessonStudentVO> list = this.baseMapper.getList(new Page<>(param.getPage(), param.getPageSize()), param);
        this.markMobiles(list.getRecords());
        return list;
    }

    @Override
    public List<LessonStudentVO> getAll(LessonStudentParamDTO param) {
        List<LessonStudentVO> list = this.baseMapper.getList(param);
        this.markMobiles(list);
        return list;
    }

    private void markMobiles(List<LessonStudentVO> voList) {
        Boolean marker = settingService.boolValue(SettingNameEnum.STUDENT_MOBILE_MARKER_SWITCH.getCode());
        if(BooleanUtils.isNotFalse(marker) && CollectionUtils.isNotEmpty(voList)) {
            for (LessonStudentVO vo: voList) {
                vo.setMobile(CommonUtil.markMobile(vo.getMobile()));
            }
        }
    }

    @Override
    public LessonStudent getByLessonIdAndStudentId(Long lessonId, Long studentId) {
        QueryWrapper<LessonStudent> qw = new QueryWrapper<>();
        qw.eq("lesson_id", lessonId).eq("student_id", studentId).last("limit 1");
        return this.getOne(qw);
    }

    @Override
    public List<LessonStudent> listByStudentId(Long studentId) {
        QueryWrapper<LessonStudent> qw = new QueryWrapper<>();
        qw.eq("student_id", studentId);
        return list(qw);
    }

    /**
     * 判断是否需要扣课时
     */
    @Override
    public Boolean calDecLessonCountByState(LessonStudent ls, SignStateEnum state) {
        // 默认旷课是否扣课时
        boolean setting = BooleanUtil.isTrue(settingService.boolValue(SettingNameEnum.DECREASE_LESSON_WHEN_ABSENCE.getCode()));
        // 学生课时记录不能是null，学生课时记录的实扣还没执行，正常签到或补签或系统旷课开启扣课时
        return (ls != null
                && (ls.getDecLessonCount() == null || ls.getDecLessonCount() == 0))
                && (SignStateEnum.NORMAL.equals(state) || SignStateEnum.LATE.equals(state) || (setting && SignStateEnum.ABSENT.equals(state)));
    }

    @Override
    public boolean evaluation(LessonEvaluateSaveDTO dto, Long teacherId) {

        Integer getScore = dto.getScore();

        LessonStudent ls = getById(dto.getId());
        ls.setScore(getScore);
        ls.setEvaluation(dto.getEvaluation());
        ls.setEvaluateTime(LocalDateTime.now());
        ls.setEvaluateTeacher(teacherId);
        boolean res = updateById(ls);

        Student student = studentMapper.selectById(ls.getStudentId());

        StudentCreditLog creditLog = new StudentCreditLog();
        creditLog.setStudentId(ls.getStudentId());
        creditLog.setCredit(getScore);
        creditLog.setChangeType(StudentCreditChangeTypeEnum.LESSON_EVALUATE);
        creditLog.setSourceId(ls.getId());
        creditLog.setSchoolId(student.getSchoolId());
        creditLog.setRemark(dto.getEvaluation());
        studentService.incCredit(creditLog);

        // 发送通知
        Lesson lesson = lessonMapper.selectById(ls.getLessonId());

        Staff staff = staffMapper.selectById(teacherId);

        LessonEvaluateBO bo = new LessonEvaluateBO();
        bo.setLessonTitle(lesson.getTitle());
        bo.setStudentName(student.getName());
        bo.setTeacherName(staff.getName());
        bo.setContent( StringUtils.isNotBlank(dto.getEvaluation())?dto.getEvaluation() : "无" );
        bo.setScoreInfo("奖励积分：" + getScore + "，剩余积分：" + student.getCredit() + "， 快去积分商城兑换礼品吧，再接再厉呦~");
        notificationService.sendToStudent(NoticeCodeEnum.STUDENT_LESSON_EVALUATED, bo, student);

        return res;

    }

    @Override
    public boolean rollbackCourseNum(List<Long> ids, Long teacherId) {

        List<LessonStudent> list = new ArrayList<>();
        for (Long id : ids) {
            // 极少情况下有的学生可能没有课时记录那么就不会有id，情况出现在课时记录还没有生成的时候。
            if (id == null) {
                throw new BizException("无法还原无签到状态的记录");
            }
            LessonStudent ls = getById(id);
            if (ls.getDecLessonCount() != null && ls.getDecLessonCount()>0 && ls.getConsumeCourseId() == null) {
                throw new BizException("无法还原未设置消费课程的的记录");
            }
            list.add(ls);
        }

        for (LessonStudent item : list) {
            Integer count = item.getDecLessonCount();
            item.setSignState(SignStateEnum.NONE);
            item.setDecLessonCount(0);
            updateById(item);
            // 有实扣的返还课时
            if (count != null && count > 0 ) {
                rollcallCancel(item, count, teacherId);
                // 记录
                studentLessonCountLogService.handleAdd(item.getStudentId(), item.getConsumeCourseId(), count, null, LessonCountChangeStageEnum.ROLLBACK, "签到状态变更", teacherId);
            }
        }
        return list.size() > 0;
    }

    /**
     * 学生签到和老师点名 生成或更新点名记录、消课逻辑
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public LessonStudent addRecord(Long lessonId, Long studentId, Lesson lesson, SignStateEnum state, SignTypeEnum type, Long teacherId) {
        Long classId = lesson.getClassId();
        int decLessonCount = Math.abs(lesson.getDecCount() == null ? 0 : lesson.getDecCount());

        LessonStudent ls = getByLessonIdAndStudentId(lessonId, studentId);
        if(ls!=null && !SignStateEnum.NONE.equals(ls.getSignState())&&!SignStateEnum.LEAVE.equals(ls.getSignState())) {
            throw new BizException("请勿重复签名或点名");
        }
        // 找到消费课时
        Long consumeCourseId = lessonStudentMapper.getConsumeCourseId(lessonId, studentId);
        if (consumeCourseId == null) {
            throw new BizException("学员未设置消费课程，无法操作。");
        }
        Student student = studentService.getById(studentId);
        if (ls != null) {
            ls.setDecLessonCount(0);
            // 如果学生上课记录里有计划消课设置，那么就以此为消课基数; 可以是0,就是不扣课时
            decLessonCount = ls.getLessonCount() != null ? Math.abs(ls.getLessonCount()) : decLessonCount;
            // 更新记录时(修改出勤状态时) 检查之前是否扣过课时,如果扣过,那么就不扣了,如果没有扣过,则检查扣多少
            if (decLessonCount > 0 && calDecLessonCountByState(ls, state)) {
                StudentCourse sc = studentCourseService.decLessonCount(consumeCourseId, student, decLessonCount);
                ls.setDecLessonCount(decLessonCount);
                ls.setConsumeCourseId(consumeCourseId);
                ls.setConsumeStudentCourseId(sc.getId());
            }
            ls.setSignTime(LocalDateTime.now());
            ls.setSignType(type);
            ls.setSignState(state);
            ls.setTeacherId(teacherId);
            this.updateById(ls);
            return ls;
        } else {
            LessonStudent newLs = new LessonStudent();
            newLs.setLessonId(lessonId);
            newLs.setClassId(classId);
            newLs.setStudentId(studentId);
            newLs.setSignTime(LocalDateTime.now());
            newLs.setSignType(type);
            newLs.setSignState(state);
            newLs.setTeacherId(teacherId);
            newLs.setConsumeCourseId(consumeCourseId);
            newLs.setCounselor(student.getCounselor());
            newLs.setLessonCount(decLessonCount);
            newLs.setDecLessonCount(0);
            // 新增签到记录时 或者 强制扣课时时, 会扣减课时
            if (decLessonCount > 0 && calDecLessonCountByState(ls, state)) {
                StudentCourse sc = studentCourseService.decLessonCount(consumeCourseId, student, decLessonCount);
                newLs.setDecLessonCount(decLessonCount);
                newLs.setConsumeStudentCourseId(sc.getId());
            }
            this.save(newLs);
            return newLs;
        }
    }

    @Override
    public LessonStudent addRecord(Long lessonId, Student student, SignStateEnum state) {
        Long studentId = student.getId();
        LessonStudent ls = getByLessonIdAndStudentId(lessonId, studentId);
        // 找到消费课时
        if (ls != null) {
            return null;
        }
        Lesson lesson = lessonMapper.selectById(lessonId);
        LessonStudent newLs = buildDefaultEntity(lesson, student, null);
        this.save( newLs );
        return newLs;
    }

    @Override
    public List<LessonStudent> addRecord(Clazz clazz) {
        LambdaQueryWrapper<ClassStudent> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ClassStudent::getClassId, clazz.getId());
        wrapper.eq(ClassStudent::getDeleted, false);
        // 班级学生记录
        List<ClassStudent> csList = classStudentMapper.selectList(wrapper);
        // 班级的学生列表
        List<Student> studentList = studentService.listByIds(csList.stream().map(ClassStudent::getStudentId).collect(Collectors.toList()));
        // 班级的课时记录，只包括未来的课
        List<Lesson> lessonList = lessonStudentMapper.listFutureLessonByClassId(clazz.getId());

        List<LessonStudent> lsList = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(csList)) {
            for (ClassStudent cs : csList) {
                Student student = new Student();
                for(Student searchStu: studentList) {
                    if(searchStu.getId().equals(cs.getStudentId())) {
                        student = searchStu;
                    }
                }
                for (Lesson lesson : lessonList) {
                    LessonStudent exist = lessonStudentMapper.getByLessonIdAndStudentId(lesson.getId(), cs.getStudentId());
                    if(exist != null) {
                        continue;
                    }
                    lsList.add(buildDefaultEntity(lesson, student, clazz));
                }
            }
        }

        if (lsList.size() > 0) {
            this.saveOrUpdateBatch(lsList);
        }
        return lsList;
    }

    @Override
    public List<LessonStudent> addRecord(Clazz clazz, Student student) {
        List<Lesson> generateLessons = lessonStudentMapper.listFutureLessonByClassId(clazz.getId());
        List<LessonStudent> exist = this.listByStudentId(student.getId());
        List<Long> existLessonIds = exist.stream().map(LessonStudent::getLessonId).collect(Collectors.toList());
        List<LessonStudent> lsList = new ArrayList<>();
        for (Lesson lesson : generateLessons) {
            if (existLessonIds.contains(lesson.getId())) {
                continue;
            }
            lsList.add(buildDefaultEntity(lesson, student, clazz));
        }
        if (lsList.size() > 0) {
             this.saveBatch(lsList);
        }
        return lsList;
    }

    /**
    * 创建一个默认实体
    * */
    public static LessonStudent buildDefaultEntity(Lesson lesson, Student student, @Nullable Clazz clazz) {
        LessonStudent newLs = new LessonStudent();
        newLs.setLessonId(lesson.getId());
        newLs.setClassId(clazz == null ? null :clazz.getId());
        newLs.setStudentId(student.getId());
        newLs.setSignState(SignStateEnum.NONE);
        newLs.setConsumeCourseId(clazz == null ? lesson.getCourseId() : clazz.getCourseId());
        newLs.setCounselor(student.getCounselor());
        newLs.setLessonCount(lesson.getDecCount());
        return newLs;
    }

    /**
     * 取消消课 返还学生的课时数
     */
    private void rollcallCancel(LessonStudent studentLesson, Integer decLessonCount, Long teacherId) {

        StudentCourse record;
        if(studentLesson.getConsumeStudentCourseId() != null) {
            record = studentCourseService.getById(studentLesson.getConsumeStudentCourseId());
        } else {
            if(studentLesson.getConsumeCourseId() == null) {
                throw new BizException("未查询到学员的消费课程");
            }
            QueryWrapper<StudentCourse> qw = new QueryWrapper<>();
            qw.eq("student_id", studentLesson.getStudentId())
                    .eq("course_id", studentLesson.getConsumeCourseId())
                    .eq("verify_state", VerifyStateEnum.APPROVE.getCode())
                    .ge("expire_date", LocalDate.now().toString())
                    .geSql("count_lesson_total", "count_lesson_refund+count_lesson_complete+"+decLessonCount)
                    .orderByDesc("priority")
                    .last("limit 1");
            record = studentCourseService.getOne(qw);
        }
        if(record == null) {
            throw new BizException("未查询到有效学员课时记录");
        }
        if (decLessonCount != null && decLessonCount > 0) {
            // 减少完成数就表示返还完毕
            record.setCountLessonComplete(record.getCountLessonComplete() - decLessonCount);
            studentCourseService.updateById(record);
        }
    }

    @Override
    @NotNull
    public LessonStudentCountsVO loadSignCounts(LessonStudentParamDTO param) {
        LessonStudentCountsVO result = baseMapper.loadSignCounts(param);

        if(result == null) {
            String courseName = "";
            if(param.getCourseId() != null) {
                Course course = courseMapper.selectById(param.getCourseId());
                courseName = course.getName();
            }
            result = new LessonStudentCountsVO();
            result.setCourseName(courseName);
            result.setCountAbsent(0);
            result.setCountSign(0);
            result.setCountLeave(0);
            result.setCountLate(0);
        }

        return result;
    }

    @Override
    public void assignSignCounts(List<StudentCourseVO> list) {
        LessonStudentParamDTO param1 = new LessonStudentParamDTO();
        for(StudentCourseVO item : list) {
            param1.setStudentId(item.getStudentId());
            param1.setCourseId(item.getCourseId());
            LessonStudentCountsVO counts = loadSignCounts(param1);
            item.setCountSign(counts.getCountSign());
            item.setCountLeave(counts.getCountLeave());
            item.setCountAbsent(counts.getCountAbsent());
            item.setCountLate(counts.getCountLate());
        }
    }

    @Override
    public Boolean deleteLessonStudent(List<Long> ids) {

        List<LessonStudent> list = listByIds(ids);
        for(LessonStudent ls : list) {
            if(!SignStateEnum.NONE.equals(ls.getSignState())) {
                throw new BizException("仅可以删除未签到的记录");
            }
        }

        return removeByIds(ids);
    }

    @Override
    public boolean changeLessonCount(LessonStudent dto) {
        if(dto.getLessonCount() == null || dto.getLessonCount() <= 0) {
            throw new BizException("消课数量有误");
        }

        LessonStudent lessonStudent = this.getById(dto.getId());
        lessonStudent.setLessonCount(dto.getLessonCount());
        return this.updateById(lessonStudent);
    }
}
